package gin_frame

import (
	"fmt"
	"sync"

	"gitee.com/yijimu/gin-frame/utils"

	"github.com/spf13/viper"
	"go.uber.org/zap"

	gormadapter "github.com/casbin/gorm-adapter/v3"

	"github.com/casbin/casbin/v2"
)

//https://blog.csdn.net/weixin_43881017/article/details/111872330?spm=1001.2014.3001.5501
//casbin_rule表  简单备注：
//ptype 为 p 时：角色id   资源    方法    租户id
//ptype 为 g 时：用户id   角色id  租户id

var (
	syncedEnforcer *casbin.SyncedEnforcer
	once           sync.Once
)

func Rbac() RbacModel {
	once.Do(func() {
		adapter, err := gormadapter.NewAdapterByDB(DBList[viper.GetString("rbac.gorm")])
		if err != nil {
			zap.L().Error(fmt.Sprintf("rbac创建adapter有误：%s; \n '1071 Specified key was too long; max key length is 1000 bytes'错误时可以，可以将表修改成innodb型", err.Error()))
		}

		// mysql适配器
		if syncedEnforcer, err = casbin.NewSyncedEnforcer(viper.GetString("rbac.modelConf"), adapter); err != nil {
			zap.L().Error("rbac配置有误：" + err.Error())
		}
	})

	return RbacModel{
		Enforcer: syncedEnforcer,
	}
}

type RbacModel struct {
	Enforcer *casbin.SyncedEnforcer
}

// Enforce 判断策略中是否存在,参数顺序要和model中request_definition一致
func (r *RbacModel) Enforce(RoleId, Path, Method, Dom string) (bool, error) {
	err := r.loadPolicy()
	if err != nil {
		return false, err
	}

	return r.Enforcer.Enforce(RoleId, Path, Method, Dom)
}

// UpdataPolicy 更新角色和资源关系(对角色和资源重新洗牌关系)，删除参数PathAndMethod列表中没有策略，添加参数PathAndMethod中有而在数据库中没有的策略
// PathAndMethod参数实例：[["/user","GET"],["/user/info","GET"]]
func (r *RbacModel) UpdataPolicy(Role string, PathAndMethod [][]string, Dom string) (bool, error) {
	err := r.loadPolicy()
	if err != nil {
		return false, err
	}

	pathAndMethodMaps := map[string][]string{}
	for _, policy := range PathAndMethod {
		pathAndMethodMaps[policy[0]+" | "+policy[1]] = policy
	}

	policyTableMaps := map[string][]string{}
	policyTable := r.Enforcer.GetFilteredPolicy(0, Role, "", "", Dom) //[[admin /user/info GET 2] [admin /user/nihao GET 2] [admin /user/ni GET 2]]
	for _, policy := range policyTable {
		policyTableMaps[policy[1]+" | "+policy[2]] = []string{policy[1], policy[2]}
	}

	for k, v := range pathAndMethodMaps {
		if _, ok := policyTableMaps[k]; !ok {
			_, _ = r.Enforcer.AddPolicy(Role, v[0], v[1], Dom)
		}
	}

	for k, v := range policyTableMaps {
		if _, ok := pathAndMethodMaps[k]; !ok {
			_, _ = r.Enforcer.RemovePolicy(Role, v[0], v[1], Dom)
		}
	}

	return true, nil
}

// UpdataRole 更新用户角色关系(对用户角色重新洗牌关系)，删除参数Roles列表中没有的角色，添加参数Roles中有而在数据库中没有的角色
func (r *RbacModel) UpdataRole(User string, Roles []string, Dom string) (bool, error) {
	err := r.loadPolicy()
	if err != nil {
		return false, err
	}

	rolesTable, err := r.Enforcer.GetRolesForUser(User, Dom) //获取用户所有已拥有的角色(数据库中)

	for _, role := range rolesTable {
		if !utils.IsContain(Roles, role) { //删除参数Roles列表中没有的角色
			_, _ = r.Enforcer.DeleteRoleForUser(User, role, Dom)
		}
	}

	for _, role := range Roles {
		if !utils.IsContain(rolesTable, role) { //删除参数Roles列表中没有的角色
			_, _ = r.Enforcer.AddRoleForUser(User, role, Dom)
		}
	}

	return true, nil
	//return r.Enforcer.AddRolesForUser(User, Roles, Dom) //如果用户之前就拥有了Roles中的一个角色，那其他的也添加不了
}

func (r *RbacModel) loadPolicy() error {
	return r.Enforcer.LoadPolicy()
}
